package com.tender.impl;

import cn.hutool.core.lang.Assert;
import com.tender.ShiroUserBridgeBizService;
import com.tender.domain.*;
import com.tender.dto.ShiroResourceDTO;
import com.tender.dto.ShiroRoleDTO;
import com.tender.dto.ShiroUserDTO;
import com.tender.service.*;
import com.tender.util.ShiroUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.tender.utils.DataUtils;
import org.springframework.util.CollectionUtils;
import com.tender.service.cache.ShiroRelationCache;
import com.tender.constants.Constants;

import java.util.ArrayList;
import java.util.List;

@Service
public class ShiroUserBridgeBizServiceImpl implements ShiroUserBridgeBizService {

    @Autowired
    private ShiroResourceService shiroResourceService;

    @Autowired
    private ShiroRoleResourceService shiroRoleResourceService;

    @Autowired
    private ShiroRoleService shiroRoleService;

    @Autowired
    private ShiroUserRoleService shiroUserRoleService;

    @Autowired
    private ShiroUserService shiroUserService;

    @Autowired
    private ShiroRelationCache shiroRelationCache;

    @Override
    public ShiroUser findUserInfoByLoginName(String loginName) {
        Assert.notBlank(loginName, "用户登录账户，不能为空");

        ShiroUser shiroUser = shiroRelationCache.findUserInfoFromCache(loginName);
        if (null != shiroUser) {
            // 缓存命中，直接返回
            return shiroUser;
        }

        ShiroUserCondition param = new ShiroUserCondition();
        param.setLoginName(loginName);

        shiroUser = shiroUserService.selectOne(param);

        // 缓存用户信息
        if (null != shiroUser) {
            shiroRelationCache.cacheUserInfo(shiroUser, Constants.FIVE_MINUTES_2_SECONDS);
        }

        return shiroUser;
    }

    @Override
    public AuthorizationInfo getAuthorizationInfo(ShiroUserDTO shiroUserDTO) {
        Assert.notNull(shiroUserDTO, "shiroUserDTO对象，不能为空");

        List<ShiroRoleDTO> roleDTOList = findRoleList(shiroUserDTO.getRowId());
        //查询用户对应的角色标识

        //查询用户对于的资源标识
        List<ShiroResourceDTO> resourceDTOList = findResourceList(shiroUserDTO.getRowId());
        //构建鉴权信息对象
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addRoles(DataUtils.transform(roleDTOList, ShiroRoleDTO::getRoleName));
        simpleAuthorizationInfo.addStringPermissions(DataUtils.transform(resourceDTOList, ShiroResourceDTO::getResourceName));

        return simpleAuthorizationInfo;
    }

    @Override
    public List<ShiroRoleDTO> findRoleList(Long userRowId) {
        Assert.notNull(userRowId, "用户rowId，不能为空");

        // 先查缓存，存在则返回
        List<ShiroRole> shiroRoles = shiroRelationCache.findRoleListFromCache(userRowId);

        if (!CollectionUtils.isEmpty(shiroRoles)) {
            return DataUtils.transform(shiroRoles, item -> DataUtils.copyProperties(item, ShiroRoleDTO.class));
        }

        ShiroUserRoleCondition param = new ShiroUserRoleCondition();
        param.setUserRowId(userRowId);
        List<ShiroUserRole> shiroUserRoles = shiroUserRoleService.selectList(param);
        if (CollectionUtils.isEmpty(shiroUserRoles)) {
            // 缓存空 ShiroRole 集合
            shiroRelationCache.cacheRoleList(userRowId, new ArrayList<>(), Constants.FIVE_MINUTES_2_SECONDS);
            return new ArrayList<>();
        }

        ShiroRoleCondition shiroRoleCondition = new ShiroRoleCondition();
        shiroRoleCondition.setRoleRowIdList(DataUtils.transform(shiroUserRoles, ShiroUserRole::getRoleRowId));
        shiroRoles = shiroRoleService.selectList(shiroRoleCondition);

        // 缓存结果
        shiroRelationCache.cacheRoleList(userRowId, shiroRoles, Constants.FIVE_MINUTES_2_SECONDS);

        return DataUtils.transform(shiroRoles, item -> DataUtils.copyProperties(item, ShiroRoleDTO.class));
    }

    @Override
    public List<ShiroResourceDTO> findResourceList(Long userRowId) {
        Assert.notNull(userRowId, "用户rowId，不能为空");

        List<ShiroResource> shiroResources = shiroRelationCache.findResourceListFromCache(userRowId);

        if (!CollectionUtils.isEmpty(shiroResources)) {
            return DataUtils.transform(shiroResources, item -> DataUtils.copyProperties(item, ShiroResourceDTO.class));
        }
        List<ShiroRoleDTO> roleList = findRoleList(userRowId);
        if (CollectionUtils.isEmpty(roleList)) {
            // 缓存空 ShiroResource 集合
            shiroRelationCache.cacheResourceList(userRowId, new ArrayList<>(), Constants.FIVE_MINUTES_2_SECONDS);
            return new ArrayList<>();
        }

        ShiroRoleResourceCondition roleResourceParam = new ShiroRoleResourceCondition();
        roleResourceParam.setRoleRowIdList(DataUtils.transform(roleList, ShiroRoleDTO::getRowId));
        List<ShiroRoleResource> shiroRoleResources = shiroRoleResourceService.selectList(roleResourceParam);
        if (CollectionUtils.isEmpty(shiroRoleResources)) {
            // 缓存空 ShiroResource 集合
            shiroRelationCache.cacheResourceList(userRowId, new ArrayList<>(), Constants.FIVE_MINUTES_2_SECONDS);
            return new ArrayList<>();
        }

        ShiroResourceCondition resourceParam = new ShiroResourceCondition();
        resourceParam.setResourceRowIdList(DataUtils.transform(shiroRoleResources, ShiroRoleResource::getResourceRowId));
        shiroResources = shiroResourceService.selectList(resourceParam);

        // 缓存空 ShiroResource 集合
        shiroRelationCache.cacheResourceList(userRowId, shiroResources, Constants.FIVE_MINUTES_2_SECONDS);

        return DataUtils.transform(shiroResources, item -> DataUtils.copyProperties(item, ShiroResourceDTO.class));
    }

    @Override
    public void clearCache(ShiroUserDTO shiroUserDTO) {
        if (null != shiroUserDTO && StringUtils.isNotBlank(shiroUserDTO.getLoginName())) {
            String sessionId = ShiroUtil.getShiroSessionId();
            String loginName = shiroUserDTO.getLoginName();
            Long userRowId = shiroUserDTO.getRowId();

            String userInfoCacheKey = Constants.SHIRO + Constants.CONNECT_STR
                    + Constants.USER + Constants.CONNECT_STR
                    + loginName;

            String roleInfoListCacheKey = Constants.SHIRO + Constants.CONNECT_STR
                    + Constants.ROLE + Constants.CONNECT_STR
                    + userRowId + Constants.CONNECT_STR + sessionId;

            String resourceInfoListCacheKey = Constants.SHIRO + Constants.CONNECT_STR
                    + Constants.RESOURCE + Constants.CONNECT_STR
                    + userRowId + Constants.CONNECT_STR + sessionId;

            shiroRelationCache.clearCache(userInfoCacheKey, roleInfoListCacheKey, resourceInfoListCacheKey);
        }
    }

}
